package com.yunsc.media.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yunsc.common.exception.YunscException;
import com.yunsc.common.properties.MinioProperties;
import com.yunsc.media.entity.YunscUploadFileEntity;
import com.yunsc.media.service.UploadService;
import com.yunsc.media.service.YunscUploadFileService;
import com.yunsc.media.utils.FormatUploadFile;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.UploadObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@Service
public class UploadServiceImpl implements UploadService {
    @Resource
    private MinioClient minioClient;
    @Resource
    private MinioProperties minioProperties;

    @Resource
    private YunscUploadFileService yunscUploadFileService;



    @Override
    @Transactional
    public YunscUploadFileEntity uploadFileEntity(MultipartFile file, Integer groupId,
                                                  Integer channel, String folder, Integer userId, Integer storeId) {
        String originalFilename = file.getOriginalFilename(); // 文件原始名称
        if (originalFilename == null) {
            return null;
        }
        byte[] bytes;
        try {
            bytes = file.getBytes();
        } catch (IOException e) {
            log.error("读取文件字节失败", e);
            throw new YunscException("读取文件字节失败");
        }
        if (StringUtils.isEmpty(folder)) {
            // 文件目录不存在，自动生成目录
            folder = userId + "/";
        }
        String fileMD5 = DigestUtils.md5DigestAsHex(bytes); // 文件md5
        String fileExt = originalFilename.substring(originalFilename.lastIndexOf(".")); // 文件后缀

        String objectName = folder + fileMD5 + fileExt; // 文件存储地址
        String contentType = file.getContentType(); // 文件类型
        // 构建接受请求类
        YunscUploadFileEntity fileEntity = new YunscUploadFileEntity();
        fileEntity.setGroupId(groupId); // 群组id
        fileEntity.setChannel(channel); // 上传渠道
        fileEntity.setFileSize(file.getSize());        // 文件大小
        fileEntity.setFileName(originalFilename); // 文件名称
        fileEntity.setFileExt(fileExt.replace(".", "")); // 文件后缀，去掉"."
        fileEntity.setUploaderId(userId); // 上传者id
        fileEntity.setStoreId(storeId); // 商户id
        fileEntity.setDomain(minioProperties.getEndpoint()); // 上传域名
        fileEntity.setStorage("minio"); // 存储类型
        fileEntity.setFilePath(objectName); // 文件存储地址
        fileEntity.setFileMd(fileMD5); // 文件md5

        // 视频上传
        if (isVideo(contentType)) {
            // 1、判断文件是否存在
            YunscUploadFileEntity uploadFile = checkFile(objectName);
            if (uploadFile != null) {
                return FormatUploadFile.formatFile(uploadFile);
            }
            // 不存在
            fileEntity.setFileType(30); // 视频
            fileEntity.setBucket(minioProperties.getBucketVideoFiles()); // 存储桶
            // 2、上传文件到文件系统
            uploadVideoToStorage(file, objectName, contentType);
        } else if (isImage(contentType)) {
            // 图片上传
            // 1、判断文件是否存在
            YunscUploadFileEntity uploadFile = checkFile(objectName);
            if (uploadFile != null) {
                return FormatUploadFile.formatFile(uploadFile);
            }
            // 不存在
            fileEntity.setFileType(10); // 文件类型(10图片 20附件 30视频)
            fileEntity.setBucket(minioProperties.getBucketFiles()); // 存储桶
            // 2、上传文件到文件系统
            uploadImageToStorage(file, objectName, contentType);
        } else {
            // 其他文件上传
            return null;
        }
        // 3、保存文件信息到数据库
        yunscUploadFileService.save(fileEntity);

        return FormatUploadFile.formatFile(fileEntity);
    }




    @Override
    @Transactional
    public String uploadFileBytes(byte[] bytes, Integer groupId,
                                                  Integer channel, String folder, String fileName,
                                                 Integer userId) {
        if (StringUtils.isEmpty(folder)) {
            // 文件目录不存在，自动生成目录
            folder = userId + "/";
        }

        String objectName = folder + fileName; // 文件存储地址

        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                .bucket(minioProperties.getBucketOtherFiles())
                .object(objectName)     // 存储桶里的对象名称。
                .stream(inputStream, inputStream.available(), -1) // 要上传的流、大小
                .contentType("application/octet-stream")
                .build();
        try {
            minioClient.putObject(putObjectArgs);
        } catch (Exception e) {
            log.error("上传到文件系统出错:{}", e.getMessage());
            throw new YunscException("上传文件到文件系统失败");
        }
        return minioProperties.getBucketOtherFiles() + "/" + objectName;
    }

    /**
     * 上传视频到minio
     *
     * @param file        上传的文件
     * @param objectName  文件存储地址
     * @param contentType 文件类型
     */
    private void uploadVideoToStorage(MultipartFile file, String objectName, String contentType) {
        // 创建临时文件
        File tempFile;
        try {
            tempFile = File.createTempFile("minio", "temp");
            // 拷贝文件到临时文件
            file.transferTo(tempFile);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("创建临时文件失败");
            throw new YunscException("创建临时文件失败");
        }
        // 获取临时文件绝对路径
        String absolutePath = tempFile.getAbsolutePath();
        // 上传文件到minio
        try {
            UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
                    .bucket(minioProperties.getBucketVideoFiles())
                    .object(objectName)
                    .filename(absolutePath) // 本地文件路径
                    .contentType(contentType) // 文件类型
                    .build();
            minioClient.uploadObject(uploadObjectArgs);
            log.info("上传文件到 minio 成功,bucket:{},objectName:{}", minioProperties.getBucketVideoFiles(), objectName);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传文件到 minio 出错,bucket:{},objectName:{},错误原因:{}", minioProperties.getBucketVideoFiles(), objectName, e.getMessage(), e);
            throw new YunscException("上传文件到文件系统失败");
        } finally {
            // 删除临时文件
            tempFile.delete();
        }
    }

    /**
     * 上传图片到minio
     *
     * @param file        上传的文件
     * @param objectName  文件存储地址
     * @param contentType 文件类型
     */
    private void uploadImageToStorage(MultipartFile file, String objectName, String contentType) {
        // 2.上传到minio     * 上传图片文件的通用接口(putObjectArgs方法)
        //      * 适合上传小文件，上传速度快
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(minioProperties.getBucketFiles())
                    .object(objectName)     // 存储桶里的对象名称。
                    .stream(file.getInputStream(), file.getSize(), -1) // 要上传的流、大小
                    .contentType(contentType)
                    .build();
            minioClient.putObject(putObjectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            throw new YunscException("上传文件到文件系统失败");
        }
    }

    public YunscUploadFileEntity checkFile(String fileMd5) {
        // 查询文件是否已存在数据库
        LambdaQueryWrapper<YunscUploadFileEntity> lqw = new LambdaQueryWrapper<>();
        lqw.eq(YunscUploadFileEntity::getFileMd, fileMd5);
        YunscUploadFileEntity fileEntity = yunscUploadFileService.getOne(lqw);
        if (fileEntity != null) {
            // 文件已存在，直接返回
            log.info("文件已存在");
            return FormatUploadFile.formatFile(fileEntity);
        }
        // 文件不存在
        return null;
    }

    /**
     * 自动生成目录
     *
     * @param year  是否包含年
     * @param month 是否包含月
     * @param day   是否包含日
     * @return
     */
    private String getFileFolder(boolean year, boolean month, boolean day) {
        StringBuffer stringBuffer = new StringBuffer();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = dateFormat.format(new Date());
        String[] split = dateString.split("-");
        if (year) {
            stringBuffer.append(split[0]).append("/");
        }
        if (month) {
            stringBuffer.append(split[1]).append("/");
        }
        if (day) {
            stringBuffer.append(split[2]).append("/");
        }
        return stringBuffer.toString();
    }


    // 判断是否是图片
    private boolean isImage(String contentType) {
        return contentType != null && contentType.contains("image");
    }

    // 判断是否是视频
    private boolean isVideo(String contentType) {
        return contentType != null && contentType.contains("mp4");
    }



}